﻿using WenSkin.Json;
using WenSkin.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using WenSkin.DLL;
using System.Runtime.InteropServices;

namespace WenSkin
{
    public partial class WenConfig
    {

        public WenConfig(ConfigFormatEnum format = ConfigFormatEnum.Xml) : this("WenAutoConfig", format) { }
        public WenConfig(string configName, ConfigFormatEnum format = ConfigFormatEnum.Xml) : this(Application.StartupPath, configName, format) { }
        public WenConfig(string path, string configName, ConfigFormatEnum format = ConfigFormatEnum.Xml)
        {
            this.Path = path;
            this.ConfigFormat = format;
            ConfigName = configName;
            Load();
        }

        public string this[string group, string name] { get => Groups[group, name]; set => Groups[group, name] = value; }
        public WenConfigGroup this[string group] { get => Groups[group]; }

        #region 委托

        [Category("Wen"), Description("保存数据")]
        public event WenConfigEventHandler SaveChanged;

        [ComVisible(true)]
        public delegate void WenConfigEventHandler(object sender, WenConfigEventArgs e);
        public class WenConfigEventArgs : EventArgs
        {
            private readonly WenConfig wenConfig;

            public WenConfigEventArgs(WenConfig config)
            {
                this.wenConfig = config;
            }

            public WenConfig WenConfig => wenConfig;
        }
        #endregion

        #region 私有属性
        private WenConfigGroupList groups;
        #endregion

        #region 公有属性
        public WenConfigForm Form => new WenConfigForm(this);
        public WenConfigControl Control => new WenConfigControl(this);
        public WenConfigGroupList Groups => groups ??= new WenConfigGroupList(this);
        public string ConfigName { get; private set; } = "WenAutoConfig";
        //是否使用xml
        public ConfigFormatEnum ConfigFormat { get; private set; } = ConfigFormatEnum.Xml;
        //文件路径
        public string FilePath => $"{this.Path}\\{ConfigName}.{ConfigFormat.ToString().ToLower()}";

        public string Path { get; private set; }

        public enum ConfigFormatEnum
        {
            Xml,
            Json
        }
        #endregion

        //打开配置文件
        public WenConfig Load()
        {
            Load(FilePath);
            return this;
        }
        public void Load(string path)
        {
            if (!File.Exists(path))
                return;
            switch (ConfigFormat)
            {
                case ConfigFormatEnum.Xml:
                    LoadXml(path);
                    break;
                case ConfigFormatEnum.Json:
                    LoadJson(path);
                    break;
            }
        }

        private void LoadXml(string path)
        {
            XmlDocument xml = new XmlDocument();
            xml.Load(path);

            if (xml.SelectNodes("Groups/Group") is XmlNodeList gxns)
            {
                In(gxns, Groups);
            }

            static void In(XmlNodeList nodeList, WenConfigGroupList groups)
            {
                foreach (XmlNode gxn in nodeList)
                {
                    WenConfigGroup group = new WenConfigGroup(groups)
                    {
                        Name = gxn.SelectSingleNode("Name")?.InnerText,
                        Info = gxn.SelectSingleNode("Info")?.InnerText,
                        AllowAdd = gxn.SelectSingleNode("AllowAdd")?.InnerText.ToLower() == "true",
                        Visible = (gxn.SelectSingleNode("Visible")?.InnerText.ToLower() ?? "true") == "true",
                    };
                    if (gxn.SelectSingleNode("Items") is XmlNode configValue)
                    {
                        foreach (XmlNode xn in configValue.SelectNodes("Item"))
                        {
                            WenConfigItem item = new WenConfigItem(group.Items)
                            {
                                Name = xn.SelectSingleNode("Name")?.InnerText,
                                Info = xn.SelectSingleNode("Info")?.InnerText,
                                Ecc = xn.SelectSingleNode("Ecc")?.InnerText.ToLower() == "true",
                                ReadOnly = xn.SelectSingleNode("ReadOnly")?.InnerText.ToLower() == "true",
                                Delete = xn.SelectSingleNode("Delete")?.InnerText.ToLower() == "true",
                                Visible = (xn.SelectSingleNode("Visible")?.InnerText.ToLower() ?? "true") == "true",
                                Tip = xn.SelectSingleNode("Tip")?.InnerText,
                                ComboBox = xn.SelectSingleNode("ComboBox")?.InnerText.ToLower() == "true",
                                DialogFile = xn.SelectSingleNode("DialogFile")?.InnerText.ToLower() == "true",
                                DiaLogFolder = xn.SelectSingleNode("DiaLogFolder")?.InnerText.ToLower() == "true",
                                Filter = xn.SelectSingleNode("Filter")?.InnerText,
                            };

                            if (xn.SelectSingleNode("Value") is XmlNode itemValue)
                            {
                                if (item.Ecc)
                                    item.Value = itemValue.InnerText.ToAESDecrypt();
                                else
                                    item.Value = itemValue.InnerText;
                            }

                            if (xn.SelectSingleNode("Items") is XmlNode nodeitem)
                            {
                                foreach (XmlNode node in nodeitem.SelectNodes("Item"))
                                {
                                    item.Items.Add(node.InnerText);
                                }
                            }

                            group.Items.Add(item);
                        }
                    }
                    if (gxn.SelectNodes("Groups/Group") is XmlNodeList gxns)
                    {
                        In(gxns, group.Groups);
                    }
                    groups.Add(group);
                }
            }
        }

        private void LoadJson(string path)
        {
            string str = File.ReadAllText(path);
            var wen = JsonConvert.DeserializeObject<JObject>(str);


            if (wen["Groups"] is JArray gxns)
            {
                In(gxns, Groups);
            }

            static void In(JArray nodeList, WenConfigGroupList groups)
            {
                foreach (var gxn in nodeList)
                {
                    WenConfigGroup group = new WenConfigGroup(groups)
                    {
                        Name = gxn["Name"]?.ToString(),
                        Info = gxn["Info"]?.ToString(),
                        AllowAdd = gxn["AllowAdd"].Value<bool>(),
                        Visible = gxn["Visible"].Value<bool>(),
                    };
                    if (gxn["Items"] is JArray configValue)
                    {
                        foreach (var xn in configValue)
                        {
                            WenConfigItem item = new WenConfigItem(group.Items)
                            {
                                Name = xn["Name"].Value<string>(),
                                Info = xn["Info"].Value<string>(),
                                Ecc = xn["Ecc"].Value<bool>(),
                                ReadOnly = xn["ReadOnly"].Value<bool>(),
                                Delete = xn["Delete"].Value<bool>(),
                                Visible = xn["Visible"].Value<bool>(),
                                Tip = xn["Tip"].Value<string>(),
                                ComboBox = xn["ComboBox"].Value<bool>(),
                                DialogFile = xn["DialogFile"].Value<bool>(),
                                DiaLogFolder = xn["DiaLogFolder"].Value<bool>(),
                                Filter = xn["Filter"].Value<string>(),
                            };

                            if (xn["Value"].Value<string>() is string itemValue)
                            {
                                if (item.Ecc)
                                    item.Value = itemValue.ToAESDecrypt();
                                else
                                    item.Value = itemValue;
                            }

                            if (xn["Items"] is JArray nodeitem)
                            {
                                foreach (var node in nodeitem)
                                {
                                    item.Items.Add(node.Value<string>());
                                }
                            }

                            group.Items.Add(item);
                        }
                    }
                    if (gxn["Groups"] is JArray gxns)
                    {
                        In(gxns, group.Groups);
                    }
                    groups.Add(group);
                }
            }
        }
        //检查路径是否存在，不存在保存一下，重建立一次

        public void Show()
        {
            this.Form.Show();
        }

        public void ShowDialog()
        {
            this.Form.ShowDialog();
        }

        //保存配置文件
        public void Save()
        {
            switch (ConfigFormat)
            {
                case ConfigFormatEnum.Xml:
                    SaveXml(this.FilePath);
                    break;
                case ConfigFormatEnum.Json:
                    SaveJson(this.FilePath);
                    break;
            }
        }

        public XmlDocument SaveXml(string path)
        {
            WenXmlDocument xml = new WenXmlDocument();

            //新方法保存xml文档
            XmlDeclaration dec = xml.CreateXmlDeclaration("1.0", "utf-8", null);
            xml.AppendChild(dec);
            XmlElement root = xml.CreateElement("Groups");
            xml.AppendChild(root);

            Out(this.Groups, root);

            void Out(WenConfigGroupList groups, XmlElement element)
            {
                foreach (var g in groups)
                {
                    XmlElement xmlg = xml.CreateElement("Group");

                    xmlg.AppendChild(xml.CreateXmlElementText("Name", g.Name));
                    xmlg.AppendChild(xml.CreateXmlElementText("Info", g.Info));
                    if (g.AllowAdd)
                        xmlg.AppendChild(xml.CreateXmlElementText("AllowAdd", g.AllowAdd.ToString()));
                    if (!g.Visible)
                        xmlg.AppendChild(xml.CreateXmlElementText("Visible", g.Visible.ToString()));

                    XmlElement items = xml.CreateElement("Items");
                    xmlg.AppendChild(items);

                    foreach (var item in g.Items)
                    {
                        XmlElement xmlitem = xml.CreateElement("Item");

                        xmlitem.AppendChild(xml.CreateXmlElementText("Name", item.Name));
                        xmlitem.AppendChild(xml.CreateXmlElementText("Info", item.Info));
                        if (item.ReadOnly)
                            xmlitem.AppendChild(xml.CreateXmlElementText("ReadOnly", item.ReadOnly.ToString()));
                        if (item.Delete)
                            xmlitem.AppendChild(xml.CreateXmlElementText("Delete", item.Delete.ToString()));

                        if (!item.Visible)
                            xmlitem.AppendChild(xml.CreateXmlElementText("Visible", item.Visible.ToString()));

                        if (!string.IsNullOrWhiteSpace(item.Tip))
                            xmlitem.AppendChild(xml.CreateXmlElementText("Tip", item.Tip));

                        if (item.DialogFile)
                        {
                            xmlitem.AppendChild(xml.CreateXmlElementText("DialogFile", item.DialogFile.ToString()));
                            xmlitem.AppendChild(xml.CreateXmlElementText("Filter", item.Filter));
                        }

                        if (item.DiaLogFolder)
                        {
                            xmlitem.AppendChild(xml.CreateXmlElementText("DiaLogFolder", item.DialogFile.ToString()));
                        }

                        if (item.Ecc)
                        {
                            xmlitem.AppendChild(xml.CreateXmlElementText("Ecc", item.Ecc.ToString()));
                            xmlitem.AppendChild(xml.CreateXmlElementText("Value", item.Value.ToAESEncrypt()));
                        }
                        else
                        {
                            xmlitem.AppendChild(xml.CreateXmlElementText("Value", item.Value));
                        }

                        if (item.ComboBox)
                        {
                            xmlitem.AppendChild(xml.CreateXmlElementText("ComboBox", item.ComboBox.ToString()));
                            if (item.ComboBox && item.Items.Count > 0)
                            {
                                XmlElement comboxs = xml.CreateElement("Items");
                                foreach (var combox in item.Items)
                                {
                                    comboxs.AppendChild(xml.CreateXmlElementText("Item", combox));
                                }
                                xmlitem.AppendChild(comboxs);
                            }
                        }
                        items.AppendChild(xmlitem);
                    }

                    if (g.Groups.Count > 0)
                    {
                        XmlElement xgs = xml.CreateElement("Groups");
                        xmlg.AppendChild(xgs);
                        Out(g.Groups, xgs);
                    }

                    element.AppendChild(xmlg);
                }
            }
            if(!Directory.Exists(System.IO.Path.GetDirectoryName(path)))
            {
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
            }

            xml.Save(path);
            SaveChanged?.Invoke(this, new WenConfigEventArgs(this));
            return xml;
        }

        public void SaveJson(string path)
        {
            var js = new WenConfigGroupList(this);

            //加密存储数据
            OutData(this.Groups, js);

            static void OutData(WenConfigGroupList groups, WenConfigGroupList owner)
            {
                foreach (var g in groups)
                {
                    WenConfigGroup jsonConfigGroup = new WenConfigGroup(owner)
                    {
                        Name = g.Name,
                        AllowAdd = g.AllowAdd,
                        Info = g.Info,
                        Visible = g.Visible,
                    };
                    owner.Add(jsonConfigGroup);

                    foreach (var item in g.Items)
                    {
                        WenConfigItem jsonConfigItem = new WenConfigItem(jsonConfigGroup.Items)
                        {
                            ComboBox = item.ComboBox,
                            Delete = item.Delete,
                            Info = item.Info,
                            Name = item.Name,
                            ReadOnly = item.ReadOnly,
                            Visible = item.Visible,
                            Tip = item.Tip,
                            Ecc = item.Ecc,
                        };
                        jsonConfigItem.Items.AddRange(item.Items.ToArray());

                        if (item.Ecc)
                        {
                            jsonConfigItem.Value = item.Value?.ToAESEncrypt();
                        }
                        else
                            jsonConfigItem.Value = item.Value;
                        jsonConfigGroup.Items.Add(jsonConfigItem);
                    }

                    if (g.Groups.Count > 0)
                        OutData(g.Groups, jsonConfigGroup.Groups);
                }
            }

            var json = JsonConvert.SerializeObject(js, WenSkin.Json.Formatting.Indented);
            JObject j = new JObject
            {
                { "Groups", JsonConvert.DeserializeObject<JArray>(json) }
            };


            if (!Directory.Exists(System.IO.Path.GetDirectoryName(path)))
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
            File.WriteAllText(path, j.ToString());
            SaveChanged?.Invoke(this, new WenConfigEventArgs(this));
        }
        public class WenConfigGroup
        {
            private WenConfigItemList items;
            private WenConfigGroupList owner;
            private WenConfigGroupList groups;

            public WenConfigGroup(WenConfigGroupList owner)
            {
                Name = "";
                Info = "";
                AllowAdd = false;
                Visible = true;
                this.owner = owner;
            }

            public string Name { get; set; }
            public string Info { get; set; }
            public bool AllowAdd { get; set; }
            public bool Visible { get; set; }
            [JsonIgnore]
            public WenConfig WenConfig => owner.WenConfig;
            public WenConfigItemList Items => items ??= new WenConfigItemList(this);

            public WenConfigGroupList Groups => groups ??= new WenConfigGroupList(owner.WenConfig);

            public void Remove()
            {
                owner.Remove(this);
            }
        }

        public class WenConfigGroupList : List<WenConfigGroup>
        {
            public WenConfigGroupList(WenConfig owner)
            {
                this.owner = owner;
            }
            private readonly WenConfig owner;
            [JsonIgnore]
            public WenConfig WenConfig => owner;
            [JsonIgnore]
            public WenConfigGroup this[string str] => Find(a => a.Name?.ToUpper() == str.ToUpper());

            public string this[string group, string val]
            {
                get => Find(a => a.Name?.ToUpper() == group.ToUpper())?.Items.Find(a => a?.Name.ToUpper() == val.ToUpper())?.Value;
                set
                {
                    if (Find(a => a.Name?.ToUpper() == group.ToUpper())?.Items.Find(a => a?.Name.ToUpper() == val.ToUpper()) is WenConfigItem im)
                    {
                        im.Value = value;
                    }
                }
            }

            //查询是否存在自定义组,不存在自动添加
            public WenConfigGroup Add(string groupName, string info = "组", bool add = false)
            {
                if (Find(a => a.Name == groupName) is WenConfigGroup group && !add)
                {
                    return group;
                }
                else
                {
                    group = new WenConfigGroup(this)
                    {
                        Name = groupName,
                        Info = info
                    };
                    Add(group);
                    return group;
                }
            }
        }

        public class WenConfigItem
        {
            private List<string> items;
            private WenConfigItemList owner;

            public WenConfigItem(WenConfigItemList owner)
            {
                this.owner = owner;
                Name = "";
                Info = "";
                Ecc = false;
                ReadOnly = false;
                Delete = false;
                Value = "";
                Tip = "";
                Visible = true;
            }

            public string Name { get; set; }
            public string Info { get; set; }
            public bool Ecc { get; set; }
            public bool ReadOnly { get; set; }
            public bool Delete { get; set; }
            public string Value { get; set; }
            public string Tip { get; set; } = "";
            public bool Visible { get; set; }
            public bool ComboBox { get; set; }
            public bool DialogFile { get; set; }
            public bool DiaLogFolder { get; set; }
            public string Filter { get; set; } = "所有文件(*.*)|*.*";

            public List<string> Items => items ??= new List<string>();
            [JsonIgnore]
            public WenConfig WenConfig => owner.WenConfig;

            public void Remove()
            {
                owner.Remove(this);
            }
        }

        public class WenConfigItemList : List<WenConfigItem>
        {
            public WenConfigItemList(WenConfigGroup owner)
            {
                this.owner = owner;
            }
            private readonly WenConfigGroup owner;

            [JsonIgnore]
            public WenConfigGroup WenConfigGroup => owner;
            [JsonIgnore]
            public WenConfig WenConfig => owner.WenConfig;
            public WenConfigItem this[string str] => Find(a => a.Name?.ToUpper() == str.ToUpper());

            //添加自定义设置项
            public WenConfigItem Add(string itemName, string value, string info = "项", bool ecc = false, bool readOnly = false)
            {
                var item = new WenConfigItem(this)
                {
                    Name = itemName,
                    Info = info,
                    Ecc = ecc,
                    ReadOnly = readOnly,
                    Value = value,
                };
                Add(item);
                return item;
            }
        }

    }
}
